package cn.xinfei.xdecision.common.service.component.decisiontable.impl;

import cn.hutool.core.date.DateUtil;
import cn.xinfei.xdecision.common.dao.component.decisiontable.DecisionTablesVersionMapper;
import cn.xinfei.xdecision.common.model.common.PageResult;
import cn.xinfei.xdecision.common.model.common.ResponseEntityDto;
import cn.xinfei.xdecision.common.model.common.requestParam.AssemblyRuleCodeReq;
import cn.xinfei.xdecision.common.model.component.decisiontable.DecisionTables;
import cn.xinfei.xdecision.common.model.component.decisiontable.DecisionTablesDetail;
import cn.xinfei.xdecision.common.model.component.decisiontable.DecisionTablesDetailCondition;
import cn.xinfei.xdecision.common.model.component.decisiontable.DecisionTablesVersion;
import cn.xinfei.xdecision.common.model.component.decisiontable.vo.DecisionTablesResultVo;
import cn.xinfei.xdecision.common.model.component.decisiontable.vo.DecisionTablesVersionListVo;
import cn.xinfei.xdecision.common.model.component.decisiontable.vo.DecisionTablesVersionVo;
import cn.xinfei.xdecision.common.model.component.strategyout.StrategyOutput;
import cn.xinfei.xdecision.common.model.login.SessionManager;
import cn.xinfei.xdecision.common.service.common.sso.SSOService;
import cn.xinfei.xdecision.common.service.common.sso.dto.SSOUserInfoReqDto;
import cn.xinfei.xdecision.common.service.common.sso.dto.SSOUserInfoRespDto;
import cn.xinfei.xdecision.common.service.component.ComponentCommonContext;
import cn.xinfei.xdecision.common.service.component.decisiontable.*;
import cn.xinfei.xdecision.common.service.component.strategyout.StrategyOutputService;
import cn.xinfei.xdecision.common.utils.constant.SysConstant;
import cn.xinfei.xdecision.common.utils.constant.strategyx.DecisionTablesDetailConst;
import cn.xinfei.xdecision.common.utils.enums.enginex.ComponentStatusEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.ComponentTypeEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.ComponentVersionStatusEnum;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import cn.xinfei.xdecision.common.utils.util.BeanUtilsPlus;
import cn.xinfei.xdecision.common.utils.util.strategyx.CustomValueUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * (DecisionTablesVersion)表服务实现类
 */
@Service("tDecisionTablesVersionService")
@Slf4j
public class DecisionTablesVersionServiceImpl extends ServiceImpl<DecisionTablesVersionMapper, DecisionTablesVersion> implements DecisionTablesVersionService {

    @Resource
    private DecisionTablesVersionMapper versionMapper;

    @Resource
    private DecisionTablesDetailService detailService;

    @Resource
    private DecisionTablesDetailConditionService conditionService;

    @Resource
    private DecisionTablesResultService resultService;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private SSOService ssoService;

    @Resource
    private DecisionTablesService decisionTablesService;

    @Resource
    private StrategyOutputService outputService;

    @Override
    public List<DecisionTablesVersionVo> queryByDecisionTableCodeAndVersionNo(String decisionTableCode, Long versionNo) {

        LambdaQueryWrapper<DecisionTablesVersion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DecisionTablesVersion::getDecisionTablesCode, decisionTableCode);
        queryWrapper.eq(DecisionTablesVersion::getVersionNo, versionNo);
        List<DecisionTablesVersion> list = versionMapper.selectList(queryWrapper);
        List<DecisionTablesVersionVo> result=new ArrayList<>();
        for (DecisionTablesVersion version : list) {
            //以下代码与queryById重重复，如有改动 ，两处都要改
            DecisionTablesVersionVo decisionTablesVersionVo = new DecisionTablesVersionVo();
            BeanUtils.copyProperties(version, decisionTablesVersionVo);
            decisionTablesVersionVo.setVersionNoDesc("V" + decisionTablesVersionVo.getVersionNo());
            decisionTablesVersionVo.setStatusDesc(Objects.requireNonNull(ComponentVersionStatusEnum.get(decisionTablesVersionVo.getStatus())).getName());

            decisionTablesVersionVo.setLeftDetailVo(detailService.queryByDecisionTablesVersionId(version.getId(), DecisionTablesDetailConst.LEFT_DETAIL_NUM));
            decisionTablesVersionVo.setTopDetailVo(detailService.queryByDecisionTablesVersionId(version.getId(), DecisionTablesDetailConst.TOP_DETAIL_NUM));

            //查询结果集表
            DecisionTablesResultVo resultList = resultService.queryByDecisionTablesVersionId(version.getId());
            decisionTablesVersionVo.setResultSet(resultList);
            //查询策略输出
            List<StrategyOutput> strategyOutputs = outputService.queryByTactics(new StrategyOutput(version.getId(), ComponentTypeEnum.DECISION_TABLES.getType()));
            decisionTablesVersionVo.setStrategyOutputList(strategyOutputs);
            result.add(decisionTablesVersionVo);
        }
        return result;
    }

    @Override
    public DecisionTablesVersionVo queryById(Long id) {
        DecisionTablesVersion version = this.getById(id);

        //以下代码与queryByDecisionTableCodeAndVersionNo重重复，如有改动 ，两处都要改
        DecisionTablesVersionVo decisionTablesVersionVo = new DecisionTablesVersionVo();
        BeanUtils.copyProperties(version, decisionTablesVersionVo);
        decisionTablesVersionVo.setVersionNoDesc("V" + decisionTablesVersionVo.getVersionNo());
        decisionTablesVersionVo.setStatusDesc(Objects.requireNonNull(ComponentVersionStatusEnum.get(decisionTablesVersionVo.getStatus())).getName());

        decisionTablesVersionVo.setLeftDetailVo(detailService.queryByDecisionTablesVersionId(id, DecisionTablesDetailConst.LEFT_DETAIL_NUM));
        decisionTablesVersionVo.setTopDetailVo(detailService.queryByDecisionTablesVersionId(id, DecisionTablesDetailConst.TOP_DETAIL_NUM));

        //查询结果集表
        DecisionTablesResultVo resultList = resultService.queryByDecisionTablesVersionId(id);
        decisionTablesVersionVo.setResultSet(resultList);
        //查询策略输出
        List<StrategyOutput> strategyOutputs = outputService.queryByTactics(new StrategyOutput(id, ComponentTypeEnum.DECISION_TABLES.getType()));
        decisionTablesVersionVo.setStrategyOutputList(strategyOutputs);
        return decisionTablesVersionVo;
    }

    @Override
    public void removeNewVersion(Long versionId) {
        DecisionTablesVersion decisionTablesVersion = this.getById(versionId);
        if (!Objects.equals(decisionTablesVersion.getStatus(), ComponentStatusEnum.NEW.getStatus())) {
            throw new ApiException(ErrorCodeEnum.ENGINE_CLEAR_ERROR.getCode(), ErrorCodeEnum.ENGINE_CLEAR_ERROR.getMessage());
        }
        transactionTemplate.execute(status -> {
            try {
                this.remove(new LambdaQueryWrapper<DecisionTablesVersion>().eq(DecisionTablesVersion::getId, decisionTablesVersion.getId()));
                detailService.deleteByDecisionTablesVersionId(decisionTablesVersion.getId(), DecisionTablesDetailConst.LEFT_DETAIL_NUM);
                detailService.deleteByDecisionTablesVersionId(decisionTablesVersion.getId(), DecisionTablesDetailConst.TOP_DETAIL_NUM);
                resultService.deleteByDecisionTablesVersionId(decisionTablesVersion.getId());
                return Boolean.TRUE;
            } catch (Exception e) {
                status.setRollbackOnly();
                log.error("清空决策表版本信息失败，版本号==>{}", versionId, e);
                throw new ApiException(ErrorCodeEnum.DECISION_REMOVE_ERROR.getCode(), ErrorCodeEnum.DECISION_REMOVE_ERROR.getMessage());
            }
        });
    }

    @Override
    public ResponseEntityDto<PageResult<List<DecisionTablesVersionListVo>, DecisionTablesVersion>> listByDecisionTableCode(AssemblyRuleCodeReq req) {
        PageMethod.startPage(req.getPageNo(), req.getPageSize());
        List<DecisionTablesVersion> list = this.list(new LambdaQueryWrapper<DecisionTablesVersion>()
                .eq(DecisionTablesVersion::getDecisionTablesCode, req.getRuleCode()));
        List<DecisionTablesVersionListVo> decisionTablesVersionListVos =
                BeanUtilsPlus.copyElements(list, DecisionTablesVersionListVo::new);
        if (CollectionUtils.isEmpty(list)) {
            return PageResult.success(new ArrayList<>(), new PageInfo<>());
        }
        SSOUserInfoReqDto ssoUserInfoReqDto = new SSOUserInfoReqDto();
        Set<Long> userIds = new HashSet<>();
        decisionTablesVersionListVos.forEach(x -> {
            userIds.add(x.getCreateUserId());
            userIds.add(x.getUpdateUserId());
        });
        ssoUserInfoReqDto.setId(new ArrayList<>(userIds));
        Map<Long, String> sysMap = ssoService.getUserInfos(ssoUserInfoReqDto).stream()
                .collect(Collectors.toMap(SSOUserInfoRespDto::getId, SSOUserInfoRespDto::getName));
        decisionTablesVersionListVos.forEach(x ->
                x.setStatusDesc(Objects.requireNonNull(ComponentVersionStatusEnum.get(x.getStatus())).getName())
                        .setCreateUserName(sysMap.getOrDefault(x.getCreateUserId(), SysConstant.EMPTY_STRING))
                        .setUpdateUserName(sysMap.getOrDefault(x.getUpdateUserId(), SysConstant.EMPTY_STRING))
                        .setVersionNoDesc("V" + x.getVersionNo()));
        return PageResult.success(decisionTablesVersionListVos, new PageInfo<>(list));
    }

    @Override
    public List<String> queryFieldEnByVersionId(Long versionId) {
        Set<String> fieldEns = new HashSet<>();
        LambdaQueryWrapper<DecisionTablesDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DecisionTablesDetail::getVersionId, versionId);
        List<DecisionTablesDetail> list = detailService.list(queryWrapper);
        Set<Long> detailIds = new HashSet<>();
        for (DecisionTablesDetail detail : list) {
            if (detail.getFieldCode().contains(".") && !detail.getFieldCode().startsWith("%")) {
                fieldEns.add(detail.getFieldCode().split("\\.")[0]);
            } else {
                fieldEns.add(detail.getFieldCode());
            }
        }
        LambdaQueryWrapper<DecisionTablesDetailCondition> conditionWrapper = new LambdaQueryWrapper<>();
        if (detailIds.size() > 0) {
            conditionWrapper.in(DecisionTablesDetailCondition::getDetailId, detailIds);
            List<DecisionTablesDetailCondition> conditionList = conditionService.list(conditionWrapper);
            for (DecisionTablesDetailCondition condition : conditionList) {
                if (condition.getValueType() == null || condition.getValueType() == 1) {
                    continue;
                }
                if (condition.getValueType() == 2) {
                    String fieldValue = condition.getValue();
                    if (fieldValue.contains(".") && !fieldValue.startsWith("%")) {
                        fieldEns.add(fieldValue.split("\\.")[0]);
                    } else {
                        fieldEns.add(fieldValue);
                    }
                } else if (condition.getValueType() == 3) {
                    fieldEns.addAll(CustomValueUtils.getFieldEnSet(condition.getValue()));
                }
            }

        }
        return new ArrayList<>(fieldEns);
    }

    @Override
    public List<DecisionTablesVersionVo> queryVersionByTablesCodes(String decisionTablesCode, Long versionNo) {
        if (StringUtils.isEmpty(decisionTablesCode)) {
            return new ArrayList<>();
        }
        LambdaQueryWrapper<DecisionTablesVersion> lbd = new LambdaQueryWrapper<DecisionTablesVersion>()
                .eq(DecisionTablesVersion::getDecisionTablesCode, decisionTablesCode);
            lbd.eq(DecisionTablesVersion::getVersionNo, versionNo);
        List<DecisionTablesVersion> versions = this.list(lbd);
        if (CollectionUtils.isEmpty(versions)) {
            return new ArrayList<>();
        }
        //校验版本状态，新建最多只会存在一个
        Map<String, Long> collect = versions.stream()
                .filter(x -> Objects.equals(x.getStatus(), ComponentVersionStatusEnum.NEW.getStatus()))
                .collect(Collectors.groupingBy(x -> x.getDecisionTablesCode() + "_" + x.getStatus(), Collectors.counting()));
        StringBuilder stringBuilder = new StringBuilder();
        collect.forEach((key, value) -> {
            if (value > 1) {
                stringBuilder.append(key, 0, key.lastIndexOf("_")).append(";");
            }
        });
        if (stringBuilder.length() > 0) {
            throw new ApiException(
                    ErrorCodeEnum.ERROR_DECISION_VERSION.getCode()
                    , ErrorCodeEnum.ERROR_DECISION_VERSION.getMessage() + "决策表编号：" + stringBuilder);
        }
        return BeanUtilsPlus.copyElements(versions, DecisionTablesVersionVo::new);
    }

    @Override
    public List<DecisionTablesVersionVo> queryVersionByTablesCodes(List<String> decisionTablesCodes, Integer status) {
        if (CollectionUtils.isEmpty(decisionTablesCodes)) {
            return new ArrayList<>();
        }
        LambdaQueryWrapper<DecisionTablesVersion> lbd = new LambdaQueryWrapper<DecisionTablesVersion>()
                .in(DecisionTablesVersion::getDecisionTablesCode, decisionTablesCodes);
        if (Objects.nonNull(status)) {
            lbd.eq(DecisionTablesVersion::getStatus, status);
        }
        List<DecisionTablesVersion> versions = this.list(lbd);
        if (CollectionUtils.isEmpty(versions)) {
            return new ArrayList<>();
        }
        //校验版本状态，新建最多只会存在一个
        Map<String, Long> collect = versions.stream()
                .filter(x -> Objects.equals(x.getStatus(), ComponentVersionStatusEnum.NEW.getStatus()))
                .collect(Collectors.groupingBy(x -> x.getDecisionTablesCode() + "_" + x.getStatus(), Collectors.counting()));
        StringBuilder stringBuilder = new StringBuilder();
        collect.forEach((key, value) -> {
            if (value > 1) {
                stringBuilder.append(key, 0, key.lastIndexOf("_")).append(";");
            }
        });
        if (stringBuilder.length() > 0) {
            throw new ApiException(
                    ErrorCodeEnum.ERROR_DECISION_VERSION.getCode()
                    , ErrorCodeEnum.ERROR_DECISION_VERSION.getMessage() + "决策表编号：" + stringBuilder);
        }
        return BeanUtilsPlus.copyElements(versions, DecisionTablesVersionVo::new);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addVersionList(List<DecisionTablesVersionVo> versionList) {
        for (DecisionTablesVersionVo versionVo : versionList) {
            if (Boolean.FALSE.equals(saveOrUpdateVersion(versionVo))) {
                throw new ApiException(ErrorCodeEnum.DECISION_SAVE_ERROR.getCode(), ErrorCodeEnum.DECISION_SAVE_ERROR.getMessage());
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long checkAndAddVersion(DecisionTablesVersionVo version) {
        version.setStatus(ComponentVersionStatusEnum.NEW.getStatus());
        List<DecisionTablesVersionVo> decisionTablesVersionVos =
                queryVersionByTablesCodes(Collections.singletonList(version.getDecisionTablesCode()), null);
        version.setOrganId(SessionManager.getLoginAccount().getOrganId());
        //如果是初始
        if (CollectionUtils.isEmpty(decisionTablesVersionVos)) {
            version.setId(null);
            version.setVersionNo(SysConstant.ZERO);
            //装配节点类型
            version.recursionNodeType();
        } else {
            DecisionTablesVersionVo newVersion = decisionTablesVersionVos.stream()
                    .filter(x -> Objects.equals(x.getStatus(), ComponentVersionStatusEnum.NEW.getStatus()))
                    .findFirst().orElse(null);
            //如果不存在新建，版本号为最大版本号+1
            if (Objects.isNull(newVersion)) {
                DecisionTablesVersionVo maxVersion = decisionTablesVersionVos.stream()
                        .max(Comparator.comparing(DecisionTablesVersionVo::getVersionNo)).orElse(null);
                if (Objects.isNull(maxVersion)) {
                    throw new ApiException(ErrorCodeEnum.ERROR_DECISION_VERSION.getCode(), ErrorCodeEnum.ERROR_DECISION_VERSION.getMessage());
                }
                version.setVersionNo(maxVersion.getVersionNo() + 1);
                version.setDescription("新建版本");
                //版本及详情id置空
                version.recurrenceSetIdIsNull();
            } else {
                //如果存在新建的，则覆盖版本的全部信息
                version.setId(newVersion.getId());
            }
        }
        //更新规则的更新人，更新时间
        DecisionTables decisionTables = new DecisionTables();
        decisionTables.setUpdatedTime(DateUtil.now());
        decisionTables.setUpdateUserId(SessionManager.getLoginUserId());
        boolean update = decisionTablesService.update(decisionTables,
                new LambdaQueryWrapper<DecisionTables>().eq(DecisionTables::getCode, version.getDecisionTablesCode()));
        //更新版本
        if (update && Boolean.FALSE.equals(saveOrUpdateVersion(version))) {
            throw new ApiException(ErrorCodeEnum.DECISION_VERSION_NEW_ERROR);
        }
        return version.getId();
    }

    public void removeVersion(DecisionTablesVersionVo version) {
        this.removeById(version.getId());
        detailService.deleteByDecisionTablesVersionId(version.getId(), DecisionTablesDetailConst.LEFT_DETAIL_NUM);
        detailService.deleteByDecisionTablesVersionId(version.getId(), DecisionTablesDetailConst.TOP_DETAIL_NUM);
        resultService.deleteByDecisionTablesVersionId(version.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveOrUpdateVersion(DecisionTablesVersionVo version) {
        DecisionTablesVersion decisionTablesVersion = BeanUtilsPlus.copyProperties(version, DecisionTablesVersion::new);
        if (Objects.isNull(decisionTablesVersion)) {
            return Boolean.TRUE;
        }
        if (Objects.isNull(decisionTablesVersion.getId())) {
            return addVersion(version);
        }
        return updateVersion(version);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean addVersion(DecisionTablesVersionVo version) {
        //修改版本主表
        versionMapper.insert(version);
        //插入子表（detail表）数据
        detailService.insertDecisionTablesDetail(version.getId(), version.getLeftDetailVo(), DecisionTablesDetailConst.LEFT_DETAIL_NUM);
        detailService.insertDecisionTablesDetail(version.getId(), version.getTopDetailVo(), DecisionTablesDetailConst.TOP_DETAIL_NUM);
        //插入子表（result表）数据
        resultService.insertDecisionTablesResult(version.getId(), version.getResultSet());
        //添加输出字段
        outputService.insertTacticsOutput(version.getId(), version.getStrategyOutputList());
        return true;
    }

    @Override
    @Transactional
    public boolean updateVersion(DecisionTablesVersionVo version) {
        //修改版本主表
        versionMapper.updateById(version);
        //修改条件表
        detailService.updateDecisionTablesDetail(version.getId(), version.getLeftDetailVo(), DecisionTablesDetailConst.LEFT_DETAIL_NUM);
        detailService.updateDecisionTablesDetail(version.getId(), version.getTopDetailVo(), DecisionTablesDetailConst.TOP_DETAIL_NUM);
        resultService.updateDecisionTablesResult(version.getId(), version.getResultSet());
        outputService.updateTacticsOutput(version.getId(), version.getStrategyOutputList(), ComponentTypeEnum.DECISION_TABLES.getType());
        return true;
    }

    /**
     * 锁定规则
     */
    public void lockDecisionTables(DecisionTablesVersion decisionTablesVersion) {
        if (!Objects.equals(decisionTablesVersion.getStatus(), ComponentVersionStatusEnum.NEW.getStatus())) {
            throw new ApiException(ErrorCodeEnum.DECISION_LOCK_ERROR_001.getCode(),
                    ErrorCodeEnum.DECISION_LOCK_ERROR_001.getMessage());
        }
        DecisionTables decisionTables = decisionTablesService.getOne(new LambdaQueryWrapper<DecisionTables>()
                .eq(DecisionTables::getCode, decisionTablesVersion.getDecisionTablesCode()));
        if (Objects.isNull(decisionTables)) {
            throw new ApiException(ErrorCodeEnum.PARAMS_EXCEPTION.getCode(),
                    ErrorCodeEnum.PARAMS_EXCEPTION.getMessage());
        }

        //校验当前版本信息完整性
        Set<String> emptyFieldSet = new HashSet<>();
        ComponentCommonContext.checkParamNotEmpty(queryById(decisionTablesVersion.getId()), emptyFieldSet);
        if (!CollectionUtils.isEmpty(emptyFieldSet)) {
            log.info(ErrorCodeEnum.INCOMPLETE_COMPONENT_INFORMATION.getMessage() + ",versionId:{},emptyFieldSet:{}",
                    decisionTablesVersion.getId(), JSON.toJSONString(emptyFieldSet));
            throw new ApiException(ErrorCodeEnum.INCOMPLETE_COMPONENT_INFORMATION);
        }

        decisionTablesVersion.setStatus(ComponentVersionStatusEnum.ENABLE.getStatus());
        transactionTemplate.execute(status -> {
            try {
                this.updateById(decisionTablesVersion);
                decisionTables.setStatus(ComponentStatusEnum.ENABLE.getStatus());
                decisionTablesService.updateById(decisionTables);
                return Boolean.TRUE;
            } catch (Exception e) {
                status.setRollbackOnly();
                log.error("决策表锁定规则失败，版本id==>{}", decisionTablesVersion.getId(), e);
                throw new ApiException(ErrorCodeEnum.DECISION_LOCK_ERROR_002.getCode(), ErrorCodeEnum.DECISION_LOCK_ERROR_002.getMessage());
            }
        });
    }

}
